Add national geographic topo format. F rom Alex Mottram.
authorrobertl <robertl@f51c46e8-681c-474f-0cfe-069cfd0219fb>
Thu, 10 Oct 2002 17:18:32 +0000 (17:18 +0000)
committerrobertl <robertl@f51c46e8-681c-474f-0cfe-069cfd0219fb>
Thu, 10 Oct 2002 17:18:32 +0000 (17:18 +0000)
gpsbabel/tpg.c [new file with mode: 0644]

diff --git a/gpsbabel/tpg.c b/gpsbabel/tpg.c
new file mode 100644 (file)
index 0000000..603961a
--- /dev/null
@@ -0,0 +1,380 @@
+/*
+    National Geographic Topo! Waypoint
+    Contributed to gpsbabel by Alex Mottram
+    
+    Copyright (C) 2002 Alex Mottram, geo_alexm at cox-internet.com
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
+
+ */
+
+#include "defs.h"
+#include "jeeps/gpsmath.h" /* for datum conversions */
+
+#define MYNAME "TPG"
+
+#define MAXTPGSTRINGSIZE       256
+#define MAXTPGOUTPUTPINS       65535   
+
+static FILE *tpg_file_in;
+static FILE *tpg_file_out;
+
+static int i_am_little_endian;
+static int endianness_tested;
+
+static void
+test_endianness(void)
+{
+        union {
+                long l;
+                unsigned char uc[sizeof (long)];
+        } u;
+
+        u.l = 1;
+        i_am_little_endian = u.uc[0];
+
+        endianness_tested = 1;
+
+}
+
+static int
+tpg_fread(void *buff, size_t size, size_t members, FILE * fp) 
+{
+    size_t br;
+
+    br = fread(buff, size, members, fp);
+
+    if (br != members) {
+        fatal(MYNAME ": requested to read %d bytes, read %d bytes.\n", members, br);
+    }
+
+    return (br);
+}
+
+static double
+tpg_fread_double(FILE *fp)
+{
+       unsigned char buf[8];
+       unsigned char sbuf[8];
+
+       if (!endianness_tested) {
+               test_endianness();
+       }
+
+       tpg_fread(buf, 1, 8, tpg_file_in);
+       if (i_am_little_endian) {
+               return *(double *) buf;
+       }
+       sbuf[0] = buf[7];
+       sbuf[1] = buf[6];
+       sbuf[2] = buf[5];
+       sbuf[3] = buf[4];
+       sbuf[4] = buf[3];
+       sbuf[5] = buf[2];
+       sbuf[6] = buf[1];
+       sbuf[7] = buf[0];
+       return *(double *)sbuf;
+}
+
+static void
+tpg_fwrite_double(double x, FILE *fp)
+{
+       unsigned char *cptr = (unsigned char *)&x;
+       unsigned char cbuf[8];
+
+       if (!endianness_tested) {
+               test_endianness();
+       }
+       if (i_am_little_endian) {
+               fwrite(&x, 8, 1, fp);
+       } else {
+                cbuf[0] = cptr[7];
+                cbuf[1] = cptr[6];
+                cbuf[2] = cptr[5];
+                cbuf[3] = cptr[4];
+                cbuf[4] = cptr[3];
+                cbuf[5] = cptr[2];
+                cbuf[6] = cptr[1];
+                cbuf[7] = cptr[0];
+                fwrite(cbuf, 8, 1, fp);
+       }
+
+}
+
+static int
+valid_tpg_header(char * header, int len) 
+{
+    unsigned char header_bytes[] = { 0xFF, 0xFF, 0x01, 0x00, 0x0D, 
+                                     0x00, 0x43, 0x54, 0x6F, 0x70, 
+                                     0x6F, 0x57, 0x61, 0x79, 0x70, 
+                                     0x6F, 0x69, 0x6E, 0x74 };
+    if (len != 19) {
+        return (-1);
+    }
+
+    return memcmp(header_bytes, header, len);
+}
+
+static void
+tpg_rd_init(const char *fname)
+{
+       tpg_file_in = fopen(fname, "rb");
+       if (tpg_file_in == NULL) {
+               fatal(MYNAME ": Cannot open %s for reading\n", fname);
+       }
+}
+
+static void
+tpg_rd_deinit(void)
+{
+       fclose(tpg_file_in);
+}
+
+static void
+tpg_wr_init(const char *fname)
+{
+       tpg_file_out = fopen(fname, "wb");
+       if (tpg_file_out == NULL) {
+               fatal(MYNAME ": Cannot open %s for writing\n", fname);
+       }
+}
+
+static void
+tpg_wr_deinit(void)
+{
+       fclose(tpg_file_out);
+}
+
+static void
+tpg_read(void)
+{
+       char buff[MAXTPGSTRINGSIZE + 1];
+       waypoint *wpt_tmp;
+       double lat, lon, elev;
+       double amt;
+       int stringsize;
+       short int pointcount;
+
+        tpg_fread(&buff[0], 2, 1, tpg_file_in);
+        
+        pointcount = le_read16(&buff[0]);
+        
+        /* the rest of the header */
+        tpg_fread(&buff[0], 19, 1, tpg_file_in);
+
+        if (valid_tpg_header(buff, 19) != 0) {
+            fatal(MYNAME ": input file does not appear to be a valid .TPG file.\n");
+        }
+        
+        
+       while (pointcount--) {
+           wpt_tmp = xcalloc(sizeof(*wpt_tmp),1);
+
+            /* 1 bytes at start of record - string size for shortname */
+           tpg_fread(&buff[0], 1, 1, tpg_file_in);
+           
+           stringsize = buff[0];
+           
+           tpg_fread(&buff[0], stringsize, 1, tpg_file_in);
+
+           buff[stringsize] = '\0';
+           
+           wpt_tmp->shortname = xstrdup(buff);
+
+            /* for some very odd reason, signs on longitude are swapped */
+            /* coordinates are in NAD27/CONUS datum                     */
+            
+            /* 8 bytes - longitude, sign swapped  */
+           lon = tpg_fread_double(tpg_file_in);
+
+            /* 8 bytes - latitude */
+           lat = tpg_fread_double(tpg_file_in);
+           
+            /* swap sign before we do datum conversions */
+           lon *= -1.0;
+           
+            /* 2 bytes - elevation in feet */
+           tpg_fread(&buff[0], 2, 1, tpg_file_in);
+           elev = (le_read16(&buff[0]) * .3048); /* feets to meters */
+
+            /* convert incoming NAD27/CONUS coordinates to WGS84 */
+            GPS_Math_Known_Datum_To_WGS84_M(
+               lat, 
+                lon,
+                0.0,
+                &wpt_tmp->position.latitude.degrees,
+                &wpt_tmp->position.longitude.degrees,
+                &amt,
+                78);
+
+            wpt_tmp->position.altitude.altitude_meters = elev;
+            
+
+            /* 4 bytes? */
+           tpg_fread(&buff[0], 4, 1, tpg_file_in);
+
+            /* 1 bytes - string size for description */
+           tpg_fread(&buff[0], 1, 1, tpg_file_in);
+           
+           stringsize = buff[0];
+           
+           tpg_fread(&buff[0], stringsize, 1, tpg_file_in);
+           buff[stringsize] = '\0';
+           
+           wpt_tmp->description = xstrdup(buff);
+           
+           /* 2 bytes */
+           tpg_fread(&buff[0], 2, 1, tpg_file_in);
+
+           waypt_add(wpt_tmp);
+       }
+}
+
+static void
+tpg_waypt_pr(const waypoint *wpt)
+{
+       double lon, lat;
+       double amt;
+       short int elev;
+       char tbuf[64];
+       char c;
+       char *shortname;
+       char *description;
+
+        /* these unknown 4 are probably point properties (color, icon, etc..) */
+       char unknown4[] = { 0x78, 0x56, 0x34, 0x12 }; 
+
+        /* these 2 appear to be constant across test files */
+       char unknown2[] = { 0x01, 0x80 };  
+       
+        /* this output format pretty much requires a description
+         * and a shortname 
+         */
+
+        if ((! wpt->shortname) || (global_opts.synthesize_shortnames)) {
+            if (wpt->description) {
+                if (global_opts.synthesize_shortnames)
+                    shortname = mkshort(wpt->description);
+                else
+                    shortname = xstrdup(wpt->description);
+            } else {
+                /* no description available */
+                shortname = xstrdup("");
+            }
+        } else{
+            shortname = xstrdup(wpt->shortname);
+        }
+
+        if (! wpt->description) {
+            if (shortname) {
+                description = xstrdup(shortname);
+            } else {
+                description = xstrdup("");
+            }
+        } else{
+            description = xstrdup(wpt->description);
+        }
+
+        /* convert lat/long to NAD27/CONUS datum */
+        GPS_Math_WGS84_To_Known_Datum_M(
+               wpt->position.latitude.degrees,
+                wpt->position.longitude.degrees,
+                0.0,
+                &lat,
+                &lon,
+                &amt,
+                78);
+        
+
+        /* swap the sign back *after* the datum conversion */
+        lon *= -1.0;
+
+        /* convert meters back to feets */
+        elev = (short int) (wpt->position.altitude.altitude_meters * 3.2808);
+
+        /* 1 bytes stringsize for shortname */
+        c = strlen(shortname);
+        fwrite(&c, 1, 1, tpg_file_out);
+        
+        /* shortname */
+        fwrite(shortname, 1, c, tpg_file_out);
+
+        /* 8 bytes - longitude */
+        tpg_fwrite_double(lon, tpg_file_out);
+
+        /* 8 bytes - latitude */
+        tpg_fwrite_double(lat, tpg_file_out);
+
+        /* 2 bytes - elevation_feet */
+        fwrite(&elev, 1, 2, tpg_file_out);
+
+        /* 4 unknown bytes */
+        memset(&tbuf, '\0', sizeof(tbuf));
+        fwrite(&unknown4, 1, 4, tpg_file_out);
+
+        /* 1 bytes stringsize for description */
+        c = strlen(description);
+        fwrite(&c, 1, 1, tpg_file_out);
+        
+        /* description */
+        fwrite(description, 1, c, tpg_file_out);
+
+        /* and finally 2 unknown bytes */
+        fwrite(&unknown2, 1, 2, tpg_file_out);
+        
+        free (shortname);
+        free (description);
+}
+
+static void
+tpg_write(void)
+{
+        int s;
+        unsigned char uc[2];
+        unsigned char header_bytes[] = { 0xFF, 0xFF, 0x01, 0x00, 0x0D, 
+                                         0x00, 0x43, 0x54, 0x6F, 0x70, 
+                                         0x6F, 0x57, 0x61, 0x79, 0x70, 
+                                         0x6F, 0x69, 0x6E, 0x74 };
+
+        s = waypt_count();
+
+        if (global_opts.synthesize_shortnames) {
+            setshort_length(32);
+            setshort_whitespace_ok(1);
+        }
+
+        if (s > MAXTPGOUTPUTPINS) {
+            fatal(MYNAME ": attempt to output too many points (%d).  The max is %d.  Sorry.\n", s, MAXTPGOUTPUTPINS);
+        }
+
+       le_write16(&uc, s);
+
+        /* write the waypoint count */
+        fwrite(&uc, 1,  2, tpg_file_out);
+
+        /* write the rest of the header */
+        fwrite(&header_bytes, 1, 19, tpg_file_out);
+
+        waypt_disp_all(tpg_waypt_pr);
+}
+
+ff_vecs_t tpg_vecs = {
+       tpg_rd_init,
+       tpg_wr_init,
+       tpg_rd_deinit,
+       tpg_wr_deinit,
+       tpg_read,
+       tpg_write,
+};